package com.chapter7.neibulei.demo5;


/**
 * 局部内部类：在方法中定义内部类，甚至在语句块中定义内部类；
 *
 * 这种情况通常是某个方法需要创建一个类来辅助完成该方法的功能，而这个类只用在该方法中。
 *
 * 局部内部类需要配合接口来一起使用。
 */
public class LocalInnerClass2 {

    Speaker getSpeaker(String str){

        /**
         * 局部内部类限定了该类只能在局部作用域内被访问，因此是无法直
         * 接返回该类型的对象的，需要向上转型为其实现的接口类型。此外，局
         * 部内部类不能使用public和private访问说明符进行声明。
         */
         class Myspeaker implements Speaker{
             @Override
             public void speak() {
                 //在局部内部类中可以直接访问方法的参数str，这样就可以减少内部类的实例变量数。
                 System.out.println(str);
             }
         }

        //返回Speaker接口类型的对象
        return new Myspeaker();
    }


    /**
     * （1）创建LocalInnerClass类的对象lic。
     * （2）调用lic的getSpeaker方法，传入实参“Local inner class”，方法形参str有值了。
     * （3）getSpeaker方法返回Speaker接口类型的对象，该方法结束，清理方法所在的栈空间，形参str被清理。
     * （4）调用Speaker对象的speak方法，输出getSpeaker方法的形参str的值。
     *
     *
     * 嗯？形参str不是被清理了，不复存在了吗？实际上，早期版本的JDK要求局部内部类在访问本地变量时（方法
     * 形参或方法内部定义的局部变量），该本地变量必须声明为final，而我们知道final代表的是“最终的”“不可修改的”，
     * 这样该变量就变成了常量，并被保存到常量池中，简单来说，就是改变了本地变量的生存时间。
     * 背后的实现就是在编译后的内部类中自动生成了一个名字为“val$变量名”的final实例变量，
     * 然后在内部类对象创建时，将方法的参数传递给构造方法，用于初始化这个final常量。
     * 之后内部类对象访问的都是自己的这个final常量。
     * 从Java 8开始，已经不要求将本地变量必须声明为final，不过其背后的实现原理是一样的，这些工作都是由编译器来完成的。
     * @param args
     */
    public static void main(String[] args) {
        LocalInnerClass2 lic = new LocalInnerClass2();
        lic.getSpeaker("Local inner class").speak();
    }

}
